# ==============================================================================
# This file is part of GMCORE since 2019.
#
# GMCORE is a dynamical core for atmospheric model.
#
# GMCORE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY. You may contact authors for helping or cooperation.
# ==============================================================================

cmake_minimum_required(VERSION 3.5)

project(gmcore LANGUAGES Fortran C)

option(R4 "Use single precision" OFF)
option(R16 "Use quadruple precision" OFF)
option(CHECK_PARALLEL "Ensure computation order in parallel" OFF)
option(CHECK_TRACERS "Check if tracers are negative when doing advection" OFF)
option(USE_GPTL "Use GPTL to profile performance" OFF)
option(OUTPUT_H1_DTEND "Output each dynamics tendency in h1 file" OFF)

if (R4)
  add_definitions(-DREAL_KIND=4)
  set(CPPFLAGS ${CPPFLAGS} -DREAL_KIND=4)
  message(STATUS "Use single precision.")
elseif (R16)
  add_definitions(-DREAL_KIND=16)
  set(CPPFLAGS ${CPPFLAGS} -DREAL_KIND=16)
  message(STATUS "Use quadruple precision.")
else ()
  add_definitions(-DREAL_KIND=8)
endif ()
if (CHECK_PARALLEL)
  add_definitions(-DCHECK_PARALLEL)
  set(CPPFLAGS ${CPPFLAGS} -DCHECK_PARALLEL)
  message(STATUS "Ensure computation order in parallel.")
endif ()
if (CHECK_TRACERS)
  add_definitions(-DCHECK_TRACERS)
  message(STATUS "Check if tracers are negative when doing advection.")
endif ()
if (OUTPUT_H1_DTEND)
  add_definitions(-DOUTPUT_H1_DTEND)
endif ()

set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR})
if (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
  add_definitions(-DFC_IS_GNU)
  list(APPEND fortran_flags
    -ffree-line-length-none
    -fno-range-check
    -ffpe-summary=none
  )
  if (CMAKE_Fortran_COMPILER_VERSION GREATER 10)
    list(APPEND fortran_flags
      -fallow-argument-mismatch
      -fallow-invalid-boz
    )
  endif ()
  if (CMAKE_BUILD_TYPE STREQUAL "Debug")
    list(APPEND fortran_flags -O0 -fcheck=all -fno-check-array-temporaries -g -fbacktrace)
  else ()
    list(APPEND fortran_flags -Ofast)
    if (CHECK_PARALLEL)
      list(APPEND fortran_flags -fcheck=bounds)
    endif ()
  endif ()
elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel" OR CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM")
  add_definitions(-DFC_IS_INTEL)
  if (CMAKE_BUILD_TYPE STREQUAL "Debug")
    list(APPEND fortran_flags
      -no-wrap-margin
      -O0
      -check bounds
      -g
      -traceback
      -fp-model precise
    )
  else ()
    list(APPEND fortran_flags
      -no-wrap-margin
      -O3
      -align array64byte
      -fp-model precise
    )
  endif ()
endif ()

find_package(MPI)

if (DEFINED ENV{NETCDF} AND (NOT DEFINED ENV{NETCDF_ROOT}))
  set(ENV{NETCDF_ROOT} $ENV{NETCDF})
endif ()
if (DEFINED ENV{NETCDF_ROOT})
  include_directories("$ENV{NETCDF_ROOT}/include")
  link_directories("$ENV{NETCDF_ROOT}/lib")
else ()
  find_package(PkgConfig)
  if (PKG_CONFIG_FOUND)
    pkg_search_module(NETCDF REQUIRED netcdf)
    if (NETCDF_FOUND)
      include_directories(${NETCDF_INCLUDEDIR})
      link_directories(${NETCDF_LIBDIR})
    else ()
      message(FATAL_ERROR "Pkg-config could not find netcdf library!")
    endif ()
  else ()
    message(FATAL_ERROR "Unable to find pkg-config library!")
  endif ()
endif ()
set(EXTERNAL_LIBS netcdff netcdf)

if (DEFINED ENV{GPTL} AND (NOT DEFINED ENV{GPTL_ROOT}))
  set(ENV{GPTL_ROOT} $ENV{GPTL})
endif ()
if (USE_GPTL AND DEFINED ENV{GPTL_ROOT})
  message(STATUS "Found GPTL in $ENV{GPTL_ROOT}.")
  add_definitions(-DHAS_GPTL)
  set(HAS_GPTL TRUE)
  include_directories("$ENV{GPTL_ROOT}/include")
  link_directories("$ENV{GPTL_ROOT}/lib")
  list(APPEND EXTERNAL_LIBS gptlf gptl)
  if (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel" OR CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM")
    list(APPEND EXTERNAL_LIBS iomp5)
  endif ()
endif ()

if (DEFINED ENV{PAPI} AND (NOT DEFINED ENV{PAPI_ROOT}))
  set(ENV{PAPI_ROOT} $ENV{PAPI})
endif ()
if (DEFINED ENV{PAPI_ROOT})
  message(STATUS "Found PAPI in $ENV{PAPI_ROOT}.")
  add_definitions(-DHAS_PAPI)
  set(HAS_PAPI TRUE)
  include_directories("$ENV{PAPI_ROOT}/include")
  link_directories("$ENV{PAPI_ROOT}/lib")
  list(APPEND EXTERNAL_LIBS papi)
endif ()

string(REPLACE ";" " " CMAKE_Fortran_FLAGS "${fortran_flags}")

add_subdirectory(lib/container)
add_subdirectory(lib/datetime)
add_subdirectory(lib/string)
add_subdirectory(lib/fiona)
add_subdirectory(lib/flogger)

add_library(gmcore)
target_sources(gmcore PRIVATE
  src/adv_batch_mod.F90
  src/adv_mod.F90
  src/ffsl_mod.F90
  src/limiter_mod.F90
  src/ppm_mod.F90
  src/upwind_mod.F90
  src/weno_mod.F90
  src/damp_mod.F90
  src/div_damp_mod.F90
  src/vor_damp_mod.F90
  src/laplace_damp_mod.F90
  src/smag_damp_mod.F90
  src/dynamics_types_mod.F90
  src/filter_mod.F90
  src/filter_types_mod.F90
  src/gmcore_mod.F90
  src/interp_mod.F90
  src/operators_mod.F90
  src/nh_mod.F90
  src/pgf_lin97_mod.F90
  src/pgf_ptb_mod.F90
  src/pgf_mod.F90
  src/pgf_swm_mod.F90
  src/time_schemes_mod.F90
  src/tracer_mod.F90
  src/tracer_types_mod.F90
  src/hybrid_coord_ecmwf_mod.F90
  src/hybrid_coord_mod.F90
  src/hybrid_coord_ncep_mod.F90
  src/hybrid_coord_wrf_mod.F90
  src/hybrid_coord_test_mod.F90
  src/smooth_coord_mod.F90
  src/vert_coord_mod.F90
  src/latlon_mesh_mod.F90
  src/latlon_halo_mod.F90
  src/latlon_field_types_mod.F90
  src/latlon_operators_mod.F90
  src/latlon_parallel_types_mod.F90
  src/latlon_parallel_zonal_mod.F90
  src/latlon_parallel_global_mod.F90
  src/latlon_parallel_mod.F90
  src/latlon_decomp_mod.F90
  src/latlon_interp_mod.F90
  src/block_mod.F90
  src/const_mod.F90
  src/formula_mod.F90
  src/gas_mod.F90
  src/math_mod.F90
  src/namelist_mod.F90
  src/perf_mod.F90
  src/process_mod.F90
  src/sphere_geometry_mod.F90
  src/time_mod.F90
)

target_link_libraries(gmcore fortran_container fortran_datetime fiona flogger ${EXTERNAL_LIBS})
